package com.citycloud.ccuap.tc.admin.filter;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.util.AntPathMatcher;

import com.citycloud.ccuap.commons.json.JSONData;
import com.citycloud.ccuap.commons.json.JSONUtil;
import com.citycloud.ccuap.commons.sys.entity.SysUser;
import com.citycloud.ccuap.tc.admin.constant.AdminConstant;
import com.citycloud.ccuap.tc.admin.feignclient.OAuth2FCService;
import com.citycloud.ccuap.tc.common.constant.OAuth2Constant;
import com.citycloud.ccuap.tc.common.result.ResultCodeEnum;

import lombok.extern.slf4j.Slf4j;

/***
 * @className AuthorizationFilter
 * @author wangyj
 * @Date 2019/9/16 20:28
 * @description
 */
@Order(-101)
@WebFilter(filterName = "authorizationFilter", urlPatterns = { "/v1/*" })
@Slf4j
public class AuthorizationFilter implements Filter {
	public static List<String> urls = new ArrayList<>();

	@Value("${security.oauth2.client.user-logout}")
	private String logoutUrl;

	@Autowired
	private StringRedisTemplate redisTemplate;

	@Autowired
	private OAuth2FCService oauth2Service;

	private static final Pattern PATTERN = Pattern.compile("\\d+");

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		urls.add("/js/CodeData.js");
		urls.add("/js/CodeConfigData.js");

		// 下面接口为可能未纯后端调用排除掉
		urls.add("/v1/oauth2/**");

		urls.add("/v1/sys/user4sec/**");
		urls.add("/v1/sys/datac/**");

		urls.add("/v1/sys/user/permission/**");

		urls.add("/v1/security/logout");
		urls.add("/v1/sys/parameter/query");
		urls.add("/swagger-ui.html");
	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) servletRequest;
		HeaderRequestWrapper requestWrapper = new HeaderRequestWrapper(req);
		try {
			String uri = req.getServletPath();
			log.debug("权限filter -------当前请求路径:{}", uri);

			AntPathMatcher pathMatcher = new AntPathMatcher();
			for (String excludeUrl : urls) {
				if (pathMatcher.match(excludeUrl, uri)) {
					filterChain.doFilter(servletRequest, servletResponse);
					return;
				}
			}

			// 获取token
			String accessToken = StringUtils.isEmpty(req.getParameter(AdminConstant.TOKEN_KEY))
					? req.getParameter(AdminConstant.ACCESS_TOKEN_KEY)
					: req.getParameter(AdminConstant.TOKEN_KEY);
			if (StringUtils.isEmpty(accessToken)) {
				accessToken = StringUtils.isEmpty(req.getHeader(AdminConstant.TOKEN_KEY))
						? req.getHeader(AdminConstant.ACCESS_TOKEN_KEY)
						: req.getHeader(AdminConstant.TOKEN_KEY);
			}
			log.debug("权限filter -------accessToken:{}", accessToken);
			if (StringUtils.isEmpty(accessToken)) {
				String url = logoutUrl;
				putResponse(servletRequest, servletResponse, "您尚未登录系统，请进行登录！", url,
						ResultCodeEnum.TOKEN_UNAUTHORIZED.getCode());
				return;
			}

			JSONData<SysUser> jsonData = oauth2Service.getUserInfo(accessToken);
			SysUser user = jsonData.getData();
			if (user == null) {
				putResponse(servletRequest, servletResponse, "用户登录失效，可能在其它地方登录", logoutUrl,
						ResultCodeEnum.TOKEN_INVALID.getCode());
				return;
			}
			log.debug("权限filter -------user:{}", user);

			// 站点id
			String scope = StringUtils.isNotBlank(req.getParameter("scope")) ? req.getParameter("scope")
					: req.getHeader("scope");

			Matcher matcher = PATTERN.matcher(scope);
			int number = scope.length();
			if (matcher.find()) {
				number = scope.indexOf(matcher.group());
			}
			scope = scope.substring(number);
			log.debug("权限filter -------scope:{}", scope);
			Long projectId = StringUtils.isEmpty(scope) ? null : Long.parseLong(scope);
			log.info("权限filter -------projectId:{}", projectId);

			// 根据oauth2 token获取到sessionId，并设置到http head
			String sessionId = redisTemplate.opsForValue().get(OAuth2Constant.DEFAULT_KEY_NAMESPACE + accessToken + ":" + projectId);
			log.info("权限filter -------统一平台的accessToken:{}", sessionId);
			requestWrapper.addHeader(AdminConstant.CCUAP_ACCESS_TOKEN_KEY, sessionId);

			filterChain.doFilter(requestWrapper, servletResponse);
		} catch (InvalidTokenException e) {
			String refurl = req.getHeader("Referer");
			if (StringUtils.isEmpty(refurl)) {
				refurl = logoutUrl;
			}
			putResponse(servletRequest, servletResponse, "tonken无效，请重新进行登录", refurl,
					ResultCodeEnum.TOKEN_INVALID.getCode());
		}
	}

	@Override
	public void destroy() {
	}

	private void putResponse(ServletRequest servletRequest, ServletResponse servletResponse, String errorMsg,
			String url, String code) throws IOException {
		servletResponse.setCharacterEncoding("utf-8");
		servletResponse.setContentType("application/json;charset=UTF-8");
		OutputStream out = servletResponse.getOutputStream();
		PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, "utf-8"));
		String jsonString = JSONUtil.getErrorJSONString(new Long(code), errorMsg, url);
		pw.println(jsonString);
		pw.flush();
		pw.close();
	}
}
